package mock

import (
	"bytes"
	"encoding/json"
	"sort"
	"testing"

	"github.com/cloudwego/hertz/pkg/common/test/assert"
	"github.com/cloudwego/hertz/pkg/common/ut"
	"github.com/cloudwego/hertz/pkg/route"
)

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

type Integer interface {
	Signed | Unsigned
}

type Ordered interface {
	Integer | ~string
}

func JsonBody(v interface{}) *ut.Body {
	body, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}
	return &ut.Body{
		Body: bytes.NewReader(body),
		Len:  len(body),
	}
}

func JsonRequest(engine *route.Engine, method string, url string, payload interface{}, headers ...ut.Header) *ut.ResponseRecorder {
	body := JsonBody(payload)
	realHeaders := []ut.Header{
		{
			Key:   "Content-Type",
			Value: "application/json",
		},
	}
	realHeaders = append(realHeaders, headers...)
	return ut.PerformRequest(engine, method, url, body, realHeaders...)
}

func DeepEqual[T any](t *testing.T, expect T, actual T) {
	t.Helper()
	assert.DeepEqual(t, expect, actual)
}

func KSortEqual[T any, K Ordered](t *testing.T, expect []T, actual []T, key func(*T) K) {
	t.Helper()
	sort.Slice(expect, func(i int, j int) bool {
		key1 := key(&expect[i])
		key2 := key(&expect[j])
		return key1 > key2
	})
	sort.Slice(actual, func(i int, j int) bool {
		key1 := key(&actual[i])
		key2 := key(&actual[j])
		return key1 > key2
	})

	DeepEqual(t, expect, actual)
}

type Record[T Ordered] interface {
	PrimaryKey() T
}

func RecordsEqual[K Ordered, T Record[K]](t *testing.T, expect []T, actual []T) {
	t.Helper()
	KSortEqual(t, expect, actual, func(v *T) K {
		return (*v).PrimaryKey()
	})
}
